"""
视频合成服务（FFmpeg）
"""
import os
import subprocess
from pathlib import Path
from typing import Optional, List
from loguru import logger

from storyforge.models import Shot, Episode
from storyforge.config import get_config
from storyforge.utils import get_project_dir, load_json


class ComposeService:
    """合成服务"""
    
    def __init__(self):
        """初始化服务"""
        self.config = get_config()
        self.fps = self.config.compose.fps
        self.resolution = self.config.compose.resolution
    
    def compose_episode(
        self,
        project_name: str,
        episode_index: int,
        output_file: Optional[str] = None
    ) -> str:
        """
        合成剧集视频
        
        Args:
            project_name: 项目名称
            episode_index: 剧集序号
            output_file: 输出文件路径，None则自动生成
            
        Returns:
            视频文件路径
        """
        project_dir = get_project_dir(project_name, self.config.paths.projects)
        shots_file = project_dir / f"episode_{episode_index}_shots.json"
        
        shots_data = load_json(str(shots_file))
        if not shots_data:
            raise FileNotFoundError(f"镜头文件不存在: {shots_file}")
        
        shots = [Shot(**s) for s in shots_data.get("shots", [])]
        
        logger.info(f"开始合成第{episode_index}集，共{len(shots)}个镜头")
        
        # 准备输出目录
        outputs_dir = self.config.paths.outputs
        os.makedirs(outputs_dir, exist_ok=True)
        
        if not output_file:
            output_file = os.path.join(outputs_dir, f"ep{episode_index}.mp4")
        
        # 1. 准备视频片段（图片序列或视频）
        video_segments = []
        for shot in shots:
            segment_path = self._prepare_shot_segment(shot, outputs_dir, episode_index)
            if segment_path:
                video_segments.append(segment_path)
        
        if not video_segments:
            raise Exception("没有可用的视频片段")
        
        # 2. 合成视频（拼接所有片段）
        self._concat_videos(video_segments, output_file)
        
        # 3. 添加音频（如果存在）
        audio_file = os.path.join(outputs_dir, f"ep{episode_index}.wav")
        if os.path.exists(audio_file):
            self._add_audio(output_file, audio_file, output_file)
        
        # 4. 添加字幕（如果存在）
        subtitle_file = os.path.join(outputs_dir, f"ep{episode_index}.srt")
        if os.path.exists(subtitle_file):
            self._add_subtitle(output_file, subtitle_file, output_file)
        
        logger.info(f"视频合成完成: {output_file}")
        return output_file
    
    def _prepare_shot_segment(self, shot: Shot, output_dir: str, episode_index: int) -> Optional[str]:
        """
        准备镜头视频片段（生成占位符或使用真实资产）
        
        Args:
            shot: 镜头对象
            output_dir: 输出目录
            episode_index: 剧集序号
            
        Returns:
            视频片段文件路径，生成失败返回None
        """
        # 简化实现：如果有资产，使用资产；否则生成占位符
        if shot.assets:
            # 实际应该加载资产并转换为视频片段
            logger.debug(f"镜头 {shot.index} 有 {len(shot.assets)} 个资产")
        
        # 生成占位符图片序列（实际应该使用真实资产）
        segment_path = os.path.join(output_dir, f"ep{episode_index}_shot_{shot.index}.mp4")
        
        # 使用FFmpeg生成占位符视频
        duration = shot.duration or 3.0
        width, height = self.resolution.split('x')
        
        cmd = [
            "ffmpeg", "-y",
            "-f", "lavfi",
            "-i", f"color=c=black:s={self.resolution}:d={duration}",
            "-vf", f"drawtext=text='Shot {shot.index}':fontsize=60:fontcolor=white:x=(w-text_w)/2:y=(h-text_h)/2",
            "-r", str(self.fps),
            "-t", str(duration),
            segment_path
        ]
        
        try:
            subprocess.run(cmd, check=True, capture_output=True)
            return segment_path
        except subprocess.CalledProcessError as e:
            logger.error(f"生成镜头片段失败: {e}")
            return None
    
    def _concat_videos(self, video_files: List[str], output_file: str):
        """
        拼接多个视频文件为一个完整视频
        
        Args:
            video_files: 视频文件路径列表
            output_file: 输出文件路径
            
        Raises:
            subprocess.CalledProcessError: FFmpeg执行失败
        """
        if not video_files:
            return
        
        # 创建文件列表
        list_file = output_file.replace('.mp4', '_list.txt')
        with open(list_file, 'w', encoding='utf-8') as f:
            for video_file in video_files:
                f.write(f"file '{os.path.abspath(video_file)}'\n")
        
        # 使用FFmpeg concat
        cmd = [
            "ffmpeg", "-y",
            "-f", "concat",
            "-safe", "0",
            "-i", list_file,
            "-c", "copy",
            output_file
        ]
        
        try:
            subprocess.run(cmd, check=True, capture_output=True)
            os.remove(list_file)
            logger.debug(f"视频拼接完成: {output_file}")
        except subprocess.CalledProcessError as e:
            logger.error(f"视频拼接失败: {e}")
            raise
    
    def _add_audio(self, video_file: str, audio_file: str, output_file: str):
        """
        为视频添加音频轨道（包含音频归一化处理）
        
        Args:
            video_file: 视频文件路径
            audio_file: 音频文件路径
            output_file: 输出文件路径
        """
        # 音频归一化
        normalized_audio = audio_file.replace('.wav', '_norm.wav')
        cmd_normalize = [
            "ffmpeg", "-y",
            "-i", audio_file,
            "-af", "loudnorm",
            normalized_audio
        ]
        
        try:
            subprocess.run(cmd_normalize, check=True, capture_output=True)
        except subprocess.CalledProcessError as e:
            logger.warning(f"音频归一化失败，使用原音频: {e}")
            normalized_audio = audio_file
        
        # 合并视频和音频
        temp_output = output_file.replace('.mp4', '_temp.mp4')
        cmd = [
            "ffmpeg", "-y",
            "-i", video_file,
            "-i", normalized_audio,
            "-c:v", "copy",
            "-c:a", "aac",
            "-shortest",
            temp_output
        ]
        
        try:
            subprocess.run(cmd, check=True, capture_output=True)
            os.replace(temp_output, output_file)
            if normalized_audio != audio_file:
                os.remove(normalized_audio)
            logger.debug("音频已添加")
        except subprocess.CalledProcessError as e:
            logger.error(f"添加音频失败: {e}")
            if os.path.exists(temp_output):
                os.remove(temp_output)
    
    def _add_subtitle(self, video_file: str, subtitle_file: str, output_file: str):
        """
        为视频添加字幕（烧录到视频中）
        
        Args:
            video_file: 视频文件路径
            subtitle_file: 字幕文件路径（SRT格式）
            output_file: 输出文件路径
        """
        temp_output = output_file.replace('.mp4', '_temp.mp4')
        cmd = [
            "ffmpeg", "-y",
            "-i", video_file,
            "-vf", f"subtitles={subtitle_file}",
            temp_output
        ]
        
        try:
            subprocess.run(cmd, check=True, capture_output=True)
            os.replace(temp_output, output_file)
            logger.debug("字幕已添加")
        except subprocess.CalledProcessError as e:
            logger.error(f"添加字幕失败: {e}")
            if os.path.exists(temp_output):
                os.remove(temp_output)

